import ctypes
import glob
import hashlib
import os
import shutil
import sys
import threading
from multiprocessing import Process

import requests
import win32com
import winshell
from PySide2.QtGui import QFont, QIcon, Qt
from PySide2.QtNetwork import QLocalSocket, QLocalServer
from PySide2.QtWidgets import QMainWindow, QApplication, QMessageBox, QSystemTrayIcon, QMenu, \
    QAction
from qfluentwidgets import Dialog, FluentTranslator, setThemeColor, InfoBar, InfoBarPosition
from win32com.client import Dispatch

import dialog_advanced_config
import dialog_config
from UserCareSystem import MainWindow
from common.log import logger
from config import initialize_config_if_missing
from dialog_config import ConfigDialog
from interface.control_window import Ui_MainWindow
from interface.progress_dialog import ProgressDialog
from version_information import VERSION, CURRENT_VERSION_NUMBER, FILE_NAME


class SingleInstance(QApplication):
    def __init__(self, key, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._key = key
        self._server = QLocalServer(self)
        self._server.newConnection.connect(self.handle_connection)

        if self.is_running():
            # 已有实例在运行，发送信号并退出
            self.send_show_window_signal()
            sys.exit(0)  # 退出当前实例
        else:
            self._server.listen(key)

    def is_running(self):
        # 检查是否有其他实例在运行
        socket = QLocalSocket(self)
        socket.connectToServer(self._key)
        if socket.waitForConnected(1000):
            socket.disconnectFromServer()
            return True
        return False

    def send_show_window_signal(self):
        # 发送信号给已有实例
        socket = QLocalSocket(self)
        socket.connectToServer(self._key)
        if socket.waitForConnected(1000):
            socket.write(b'UserCareSystem')
            socket.waitForBytesWritten(1000)
            socket.disconnectFromServer()

    def handle_connection(self):
        socket = self._server.nextPendingConnection()
        if socket.waitForReadyRead(1000):
            data = socket.readAll().data()
            if data == b'UserCareSystem':
                self.show_main_window()
        socket.disconnectFromServer()

    def show_main_window(self):
        # 显示主窗口
        for widget in self.topLevelWidgets():
            if isinstance(widget, ControlWindow):
                if widget.isMinimized():
                    widget.showNormal()  # 恢复窗口到正常状态
                elif widget.isHidden():
                    widget.show()  # 如果窗口被隐藏，显示窗口
                widget.activateWindow()  # 激活窗口
                widget.raise_()  # 将窗口置顶
                break


def fetch_update_info():
    """从服务器获取更新信息"""
    url = "https://qn.aicache.wooxin.cn/client/update_information{}.json".format(VERSION)
    try:
        response = requests.get(url, verify=False)
    except:
        _title = "检测更新失败"
        _content = "网络环境异常，如有代理请关闭代理"
        msg_box = Dialog(_title, _content)
        msg_box.yesButton.setText("确认")
        msg_box.cancelButton.hide()
        msg_box.buttonLayout.insertStretch(1)
        if msg_box.exec():
            pass
        else:
            pass
    return response.json()


def start_new_window(title):
    app = QApplication(sys.argv)
    window = MainWindow(title)
    window.exec_()


class ControlWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.init_ui()
        self.process = {}
        self.win_1 = None
        self.win_2 = None

    def init_ui(self):
        setThemeColor("#07C160")

        # 创建系统托盘图标
        self.tray_icon = QSystemTrayIcon(self)
        self.tray_icon.setIcon(QIcon(':/assets/icon.ico'))  # 设置托盘图标
        self.tray_icon.setToolTip("用户关爱系统")
        self.tray_icon.setVisible(True)

        # 创建托盘菜单
        tray_menu = QMenu()
        restore_action = QAction("显示", self)
        restore_action.triggered.connect(self.restore_from_tray)
        tray_menu.addAction(restore_action)
        tray_menu.addSeparator()
        quit_action = QAction("退出", self)
        quit_action.triggered.connect(self.close)
        tray_menu.addAction(quit_action)

        # 设置托盘菜单
        self.tray_icon.setContextMenu(tray_menu)

        # 绑定托盘图标的激活事件
        self.tray_icon.activated.connect(self.on_tray_icon_activated)

        # 创建定时器对象
        self.qrcode_checker_timer_1 = None
        self.qrcode_checker_timer_2 = None
        self.qrcode_image_path = ["./bots/bot1/QR.png", "./bots/bot2/QR.png"]

        self.setWindowIcon(QIcon(":/assets/icon.ico"))

        self.ui.pushButton_login_1.clicked.connect(self.start_process_1)
        self.ui.pushButton_login_2.clicked.connect(self.start_process_2)
        self.ui.pushButton_edit_config.clicked.connect(self.open_config_dialog)
        self.ui.pushButton_check_update.clicked.connect(self.check_for_updates)

        try:
            # 检查exe同路径下是否有config.json文件，没有则创建
            initialize_config_if_missing()
            # 检查并删除当前路径下名为“旧版本_*”的exe文件
            self.delete_old_exe_files()
        except:
            pass

        # 设置开机自启动
        try:
            exe_path = self.get_current_exe_path()
            self.update_shortcut_in_startup(exe_path, FILE_NAME)
        except:
            pass

    def delete_old_exe_files(self):
        # 匹配所有以"旧版本_"开头的.exe文件
        files_to_delete = glob.glob('旧版本_*.exe')

        # 遍历找到的所有文件
        for file_path in files_to_delete:
            try:
                # 检查文件是否被占用
                if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
                    continue

                # 尝试删除文件
                os.remove(file_path)
                logger.info(f"已删除: {file_path}")
            except PermissionError:
                logger.error(f"删除失败: {file_path} - 错误: 权限不足")
            except FileNotFoundError:
                logger.error(f"删除失败: {file_path} - 错误: 文件未找到")
            except OSError as e:
                logger.error(f"删除失败: {file_path} - 错误: {e}")

    def start_process_1(self):
        process = Process(targert=start_new_window, args=("account_1"))
        process.start()

    def start_process_2(self):
        if self.win_2 is None:
            self.win_2 = MainWindow(self)
        self.win_2.show()

    def open_config_dialog(self):
        dialog = ConfigDialog()
        dialog.exec()

    def closeEvent(self, event):
        # 创建并显示确认退出的对话框
        self.ui.close_message = Dialog("确定退出？", "所有账号将登出", self)
        self.ui.close_message.adjustSize()
        self.ui.close_message.yesButton.setText("最小化至系统托盘")
        self.ui.close_message.cancelButton.setText("退出程序")

        if self.ui.close_message.exec():
            self.close = False
            # 忽略关闭事件
            event.ignore()
            self.hide()  # 隐藏窗口
            self.tray_icon.show()
        else:
            # 清理图片缓存
            for index in range(1, 3):
                if os.path.isfile(self.qrcode_image_path[index - 1]):
                    os.remove(self.qrcode_image_path[index - 1])
            # 接受关闭事件
            event.accept()

    def check_for_updates(self, flag=0):
        """检查是否有更新"""
        update_info = fetch_update_info()
        if update_info["version"] > CURRENT_VERSION_NUMBER:
            _title = "新版本已就绪"
            _content = f"新版本 （{update_info['version']}） 已就绪.\n{update_info['description']}\n是否现在下载更新?"
            msg_box = Dialog(_title, _content)
            msg_box.yesButton.setText("确认")
            msg_box.cancelButton.setText("稍后")

            if msg_box.exec():
                try:
                    # 创建并显示进度条对话框
                    self.progress_dialog = ProgressDialog(self)
                    self.progress_dialog.show()

                    # 创建并启动下载线程
                    self.download_thread = DownloadThread(update_info["download_url"], update_info["checksum"],
                                                          self.progress_dialog.progress_signal,
                                                          self.progress_dialog.complete_signal)
                    self.download_thread.daemon = True
                    self.download_thread.start()
                except Exception as e:
                    QMessageBox.critical(None, "更新失败", str(e))
            else:
                pass

        else:
            if flag == 0:
                _content = f"当前版本 （{update_info['version']}）已是最新版本"
                msg_box = Dialog("已是最新版本", _content)
                msg_box.yesButton.setText("确认")
                msg_box.cancelButton.hide()
                msg_box.buttonLayout.insertStretch(1)
                if msg_box.exec():
                    pass
                else:
                    pass
            else:
                pass

    def restore_from_tray(self):
        self.showNormal()
        self.activateWindow()

    def on_tray_icon_activated(self, reason):
        """处理托盘图标的激活事件"""
        if reason == QSystemTrayIcon.DoubleClick:
            self.showNormal()

    def get_current_exe_path(self):
        return os.path.abspath(sys.argv[0])

    def create_shortcut_in_startup(self, exe_path, shortcut_name):
        # 获取启动文件夹的路径
        startup_dir = winshell.startup()

        # 构建快捷方式的完整路径
        shortcut_path = os.path.join(startup_dir, f'{shortcut_name}.lnk')

        # 创建快捷方式
        shell = Dispatch('WScript.Shell')
        shortcut = shell.CreateShortCut(shortcut_path)
        shortcut.Targetpath = exe_path
        shortcut.WorkingDirectory = os.path.dirname(exe_path)
        shortcut.save()

    def update_shortcut_in_startup(self, exe_path, shortcut_name):
        try:
            # 获取启动文件夹的路径
            startup_dir = winshell.startup()

            # 构建快捷方式的完整路径
            shortcut_path = os.path.join(startup_dir, f'{shortcut_name}.lnk')

            if os.path.exists(shortcut_path):
                # 更新快捷方式
                shell = Dispatch('WScript.Shell')
                shortcut = shell.CreateShortCut(shortcut_path)
                shortcut.Targetpath = exe_path
                shortcut.WorkingDirectory = os.path.dirname(exe_path)
                shortcut.save()
                # print("快捷方式已更新")
            else:
                # 创建新的快捷方式
                self.create_shortcut_in_startup(exe_path, shortcut_name)
                # print("快捷方式已创建")
        except:
            pass

    def configure_update_success(self):
        InfoBar.success(
            title='消息',
            content="配置保存成功！",
            orient=Qt.Horizontal,
            isClosable=True,
            position=InfoBarPosition.TOP,
            duration=3000,
            parent=self,
        )

    def re_start(self):
        if not self.close:
            # 停止所有相关进程
            self.bot_run_process.stop_process(self.queue_2, 1, 1)
            self.bot_run_process.stop_process(self.queue_2, 2, 1)
            # 结束检测二维码的线程
            if self.qrcode_check_thread:
                self.qrcode_check_thread.stop_thread()
            else:
                pass
            # 清理图片缓存
            for index in range(1, 3):
                if os.path.isfile(self.qrcode_image_path[index - 1]):
                    os.remove(self.qrcode_image_path[index - 1])
            _title = "登录出错"
            _content = "检测到掉线，应用将重新启动，请重新扫码登录"
            msg_box = Dialog(_title, _content)
            msg_box.yesButton.setText("确认")
            msg_box.cancelButton.hide()
            msg_box.buttonLayout.insertStretch(1)
            if msg_box.exec():
                self.restart_application()
            else:
                pass
        else:
            pass

    def restart_application(self):
        os.execv(sys.executable, ['python'] + sys.argv)


def fetch_update_info():
    """从服务器获取更新信息"""
    url = "https://qn.aicache.wooxin.cn/client/update_information{}.json".format(VERSION)
    try:
        response = requests.get(url, verify=False)
    except:
        _title = "检测更新失败"
        _content = "网络环境异常，如有代理请关闭代理"
        msg_box = Dialog(_title, _content)
        msg_box.yesButton.setText("确认")
        msg_box.cancelButton.hide()
        msg_box.buttonLayout.insertStretch(1)
        if msg_box.exec():
            pass
        else:
            pass
    return response.json()


def verify_checksum(file_path, expected_checksum):
    """验证文件的SHA256校验和"""
    hasher = hashlib.sha256()
    with open(file_path, 'rb') as f:
        buf = f.read()
        hasher.update(buf)
    return hasher.hexdigest() == expected_checksum


def replace_and_restart(temp_file, win):
    # 获取当前运行的EXE文件的完整路径
    current_exe_path = sys.executable
    exe_dir = os.path.dirname(current_exe_path)
    exe_name = os.path.basename(current_exe_path)

    # 新EXE文件的名称和路径
    new_exe_name = "UserCareSystem{}.exe".format(VERSION)
    new_exe_path = os.path.join(exe_dir, new_exe_name)

    # 替换旧的EXE文件
    try:
        # 先备份旧的EXE文件
        backup_path = os.path.join(exe_dir, "旧版本_" + exe_name)
        shutil.move(current_exe_path, backup_path)

        # 移动新文件到正确的位置
        shutil.move(temp_file, new_exe_path)

        # 清理日志
        clear_logs()

        # 重启应用程序
        os.execv(new_exe_path, ['python'] + sys.argv + ['--create-shortcut'])
    except Exception as e:
        # 如果替换失败，恢复旧的EXE文件
        shutil.move(backup_path, current_exe_path)
        raise e


class DownloadThread(threading.Thread):
    def __init__(self, download_url, checksum, progress_signal, complete_signal):
        super().__init__()
        self.download_url = download_url
        self.checksum = checksum
        self.progress_signal = progress_signal
        self.complete_signal = complete_signal
        self.temp_file = None

    def run(self):
        # 获取当前运行的EXE文件的完整路径
        current_exe_path = sys.executable
        exe_dir = os.path.dirname(current_exe_path)
        exe_name = os.path.basename(current_exe_path)

        # 新EXE文件的名称和路径
        new_exe_name = "UserCareSystem{}.exe".format(VERSION)
        new_exe_path = os.path.join(exe_dir, new_exe_name)

        # 下载新EXE文件到临时位置
        temp_file = os.path.join(exe_dir, "temp_" + new_exe_name)

        try:
            response = requests.get(self.download_url, stream=True, verify=False)
            response.raise_for_status()

            total_size = int(response.headers.get('content-length', 0))
            with open(temp_file, 'wb') as file:
                for chunk in response.iter_content(chunk_size=8192):
                    file.write(chunk)
                    progress = int(100 * file.tell() / total_size)
                    self.progress_signal.emit(progress)

            if not verify_checksum(temp_file, self.checksum):
                raise ValueError("检验校验和失败，文件未完全下载，请稍后重试")

            self.temp_file = temp_file

        except Exception as e:
            raise e

        self.complete_signal.emit(self.temp_file, None)


def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False


def create_desktop_shortcut():
    try:
        # 获取当前脚本的绝对路径
        script_path = os.path.abspath(sys.argv[0])

        # 获取桌面路径
        desktop_path = os.path.join(os.environ['USERPROFILE'], 'Desktop')

        # 快捷方式文件的名称
        shortcut_name = "UserCareSystem.lnk"
        shortcut_path = os.path.join(desktop_path, shortcut_name)

        # 检查并删除已存在的以“UserCareSystem”开头的快捷方式
        for file_name in os.listdir(desktop_path):
            if file_name.startswith("UserCareSystem") and file_name.endswith(".lnk"):
                file_path = os.path.join(desktop_path, file_name)
                os.remove(file_path)

        # 创建Shell对象
        shell = win32com.client.Dispatch('WScript.Shell')

        # 创建快捷方式
        shortcut = shell.CreateShortcut(shortcut_path)
        shortcut.TargetPath = script_path
        shortcut.WorkingDirectory = os.path.dirname(script_path)
        shortcut.IconLocation = f"{script_path}, 0"
        shortcut.Save()
    except:
        pass


def clear_log_file(file_path):
    try:
        if os.path.exists(file_path):
            with open(file_path, 'w') as f:
                f.truncate(0)  # 清空文件内容
    except Exception as e:
        pass


def clear_logs():
    try:
        threads = []

        # 主日志文件
        file_path = "run.log"
        thread = threading.Thread(target=clear_log_file, args=(file_path,))
        thread.daemon = True
        threads.append(thread)
        thread.start()

        # 子日志文件
        for i in range(1, 3):
            file_path = f"bots/bot{i}/run_{i}.log"
            thread = threading.Thread(target=clear_log_file, args=(file_path,))
            thread.daemon = True
            threads.append(thread)
            thread.start()
    except:
        pass


def run_check_for_updates(control_window):
    control_window.check_for_updates(1)


def main_is_admin():
    if not is_admin():
        # 重新启动程序并请求管理员权限
        ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
    else:
        # 检查命令行参数，如果包含 --create-shortcut，则创建快捷方式
        if '--create-shortcut' in sys.argv or not sys.argv[1:]:
            create_desktop_shortcut()

        app = SingleInstance("UseCareSystem")

        translator = FluentTranslator()
        app.installTranslator(translator)
        font = QFont()
        font.setFamily('Microsoft YaHei')
        app.setFont(font)

        control_window = ControlWindow()
        dialog_config.control_window = control_window
        dialog_advanced_config.control_window = control_window
        control_window.show()

        try:
            control_window.check_for_updates(1)
            update_thread = threading.Thread(target=run_check_for_updates, args=(control_window,))
            update_thread.daemon = True
            update_thread.start()
        except:
            pass

        sys.exit(app.exec_())


def main():
    # 检查命令行参数，如果包含 --create-shortcut，则创建快捷方式
    if '--create-shortcut' in sys.argv or not sys.argv[1:]:
        create_desktop_shortcut()

    app = SingleInstance("UseCareSystem")

    translator = FluentTranslator()
    app.installTranslator(translator)
    font = QFont()
    font.setFamily('Microsoft YaHei')
    app.setFont(font)

    control_window = ControlWindow()
    dialog_config.control_window = control_window
    dialog_advanced_config.control_window = control_window
    control_window.show()

    try:
        control_window.check_for_updates(1)
        update_thread = threading.Thread(target=run_check_for_updates, args=(control_window,))
        update_thread.daemon = True
        update_thread.start()
    except:
        pass

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
